/*
 * This file is part of Cadmium.
 * Copyright (C) 2007-2010 Xavier Clerc.
 *
 * Cadmium is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Cadmium is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package fr.x9c.cadmium.primitives.unix;

import java.util.Calendar;
import java.util.TimeZone;

import fr.x9c.cadmium.kernel.Block;
import fr.x9c.cadmium.kernel.CodeRunner;
import fr.x9c.cadmium.kernel.Primitive;
import fr.x9c.cadmium.kernel.PrimitiveProvider;
import fr.x9c.cadmium.kernel.Value;

/**
 * This class provides implementation for time- and calendar-related primitives.
 *
 * @author <a href="mailto:cadmium@x9c.fr">Xavier Clerc</a>
 * @version 1.0
 * @since 1.0
 */
@PrimitiveProvider
public final class Gmtime {

    /**
     * No instance of this class.
     */
    private Gmtime() {
    } // end empty constructor

    /**
     * Constructs a <i>Unix.tm</i> structure from a time elapsed since January
     * 1, 1970 in seconds.
     * @param ctxt context
     * @param t time elapsed since January 1, 1970 in seconds
     * @return corresponding <i>Unix.tm</i> structure
     *         set to <i>false</i>)
     */
    @Primitive
    public static Value unix_gmtime(final CodeRunner ctxt,
                                    final Value t) {
        return createTime((long) (t.asBlock().asDouble() * Unix.MILLISECS_PER_SEC), false);
    } // end method 'unix_gmtime(CodeRunner, Value)'

    /**
     * Constructs a <i>Unix.tm</i> structure from a time elapsed since January
     *  1, 1970 in seconds.
     * @param ctxt context
     * @param t time elapsed since January 1, 1970 in seconds
     * @return corresponding <i>Unix.tm</i> structure
     *         set to <i>false</i>)
     */
    @Primitive
    public static Value unix_localtime(final CodeRunner ctxt,
                                       final Value t) {
        return createTime((long) (t.asBlock().asDouble() * Unix.MILLISECS_PER_SEC), true);
    } // end method 'unix_localtime(CodeRunner, Value)'

    /**
     * Computes the time elapsed since January 1, 1970 in seconds from
     * a <i>Unix.tm</i> structure.
     * @param ctxt context
     * @param t <i>Unix.tm</i> structure
     * @return <i>(secs, tm)</i> with tm being a normized value of <i>t</i> and
     */
    @Primitive
    public static Value unix_mktime(final CodeRunner ctxt,
                                    final Value t) {
        final Block b = t.asBlock();
        final Calendar c = Calendar.getInstance();
        c.set(Calendar.SECOND, b.get(0).asLong());
        c.set(Calendar.MINUTE, b.get(1).asLong());
        c.set(Calendar.HOUR, b.get(2).asLong());
        c.set(Calendar.DAY_OF_MONTH, b.get(3).asLong());
        c.set(Calendar.MONTH, b.get(4).asLong());
        c.set(Calendar.YEAR, b.get(5).asLong() + 1900);
        c.set(Calendar.DAY_OF_WEEK, b.get(6).asLong() + 1);
        c.set(Calendar.DAY_OF_YEAR, b.get(7).asLong() + 1);
        final long time = c.getTimeInMillis();
        final Block res = Block.createBlock(0,
                                            Value.createFromBlock(Block.createDouble(((double) time) / Unix.MILLISECS_PER_SEC)),
                                            createTime(time, true));
        return Value.createFromBlock(res);
    } // end method 'unix_mktime(CodeRunner, Value)'

    /**
     * Creates a time structure.
     * @param t time in milliseconds since January 1, 1970
     * @param locale whether to use locale
     * @return corresponding time structure of <i>Unix.tm</i> type
     */
    private static Value createTime(final long t, final boolean locale) {
        final Calendar c = Calendar.getInstance();
        c.setTimeInMillis(t);
        final Block res = Block.createBlock(9, 0);
        res.set(0, Value.createFromLong(c.get(Calendar.SECOND)));
        res.set(1, Value.createFromLong(c.get(Calendar.MINUTE)));
        res.set(2, Value.createFromLong(c.get(Calendar.HOUR)));
        res.set(3, Value.createFromLong(c.get(Calendar.DAY_OF_MONTH)));
        res.set(4, Value.createFromLong(c.get(Calendar.MONTH)));
        res.set(5, Value.createFromLong(c.get(Calendar.YEAR) - 1900));
        res.set(6, Value.createFromLong(c.get(Calendar.DAY_OF_WEEK) - 1));
        res.set(7, Value.createFromLong(c.get(Calendar.DAY_OF_YEAR) - 1));
        res.set(8, locale && TimeZone.getDefault().inDaylightTime(c.getTime())
                ? Value.TRUE
                : Value.FALSE);
        return Value.createFromBlock(res);
    } // end method 'createTime(long, boolean)'

} // end class 'Gmtime'
